STM32 map文件分析,对理解程序有很大帮助 您所在的位置:网站首页 stm32 startup文件 STM32 map文件分析,对理解程序有很大帮助

STM32 map文件分析,对理解程序有很大帮助

#STM32 map文件分析,对理解程序有很大帮助| 来源: 网络整理| 查看: 265

STM32 MAP文件浅析 1、MDK编译生成文件 MDK编译工程,会生成一些中间文件(.o .axf .map等),最终会生成HEX文件,以便下载到MCU上面执行,我们通常使用STM32工程中,output文件夹下面会生成十多种文件类型。我们这里只介绍几种重要的文件。 1、.o文件:它是由编译器编译.c/.s文件时所产生的可重定向对象文件。【注:①可重定向是指该文件包涵数据、代码,但是没有指定地址,他的地址可以由后续链接的时候进行指定,②不可重定向是指这种文件所包含的数据/代码都已经指定地址了,不能再改变】 2、.axf文件:它是由armlink链接器,将整个工程参与编译的.o文件链接成一个可执行对象文件。它是不可重定向的。有了该文件,我们就可以用仿真器来下载到MCU进行仿真调试了。 【注】各类仿真器,在进行下载调试的时候都是用的,axf文件 3、.hex文件:它是由.axf转换而来的一个可执行对象文件。.hex文件和.bin文件的区别是:.bin文件不含地址信息,全部是可执行代码;而hex文件则是包含地址信息的可执行代码。同样的.bin文件也是由.axf文件转换而来的。 我们在使用ISP软件进行程序下载的时候,一般使用的是.hex文件包含的地址信息来实现程序下载。 而我们在进行BootLoader升级的时候,一般使用的是.bin文件,地址有Bootloader程序指定。 【注】①ISP软件:In System Programable 的缩写,在系统编程的意思,通俗地讲,就是芯片已经焊接在板子上了,不用取下,就可以简单而方便地对其进行编程。 比如我们通过电脑给单片机烧录程序,就是利用了ISP技术。 工作原理:**一般通用做法是内部的存储器可以由上位机的软件通过串口来进行改写。** ②Bootloader:嵌入式设备,或多或少都需要对设备进行更新,来适应更多的需求,如果未出货的设备还可以使用下载线去下载,但是如果出货后到了客户那里,客户不可能像专业人员一样来操作下载流程,无法对设备进行升级,这时候,BootLoader的重要性就凸显出来了,它的设计就是为了设备能够进行远程升级或者只用指令升级,极大简便升级步骤,增强产品的后续维护性。【STM32 Bootloader的相关实现,等待接下来的文章再详述】 4、.htm文件:它是编译器在编译代码的时候生成的一个列表文件,包含了整个工程的静态调用图,最大用处就是可以查看栈深度(最小深度),方便设置栈的大小。.htm文件可以直接由浏览器打开。其中主要包括两部分内容: ①整个工程最大栈(Stack)深度及其调用关系。打开其中一个.htm文件我们可以看到: ![.htm文件打开后的内容](https://img-blog.csdnimg.cn/6ba1603b11524386bbed94fbdc172e02.png) 可以看到例程的最大深度是416字节,最大栈深时的调用关系为::__rt_entry_main

⇒main⇒sys_stm32_clock_init⇒HAL_RCCClockConfig⇒HAL_InitTick⇒HAL_NVIC_SetPriority⇒__NVIC_SetPriority。

不过需要注意的是,这里的最大栈深度仅仅是最低的要求(静态栈),因为它并没有统计无栈深的函数(用内存管理)、递归函数、以及无法追踪的函数(函数指针)等所包含的栈(Stack) 但是,它给我们指明了最低需求,我们在分配栈深度的时候,就可以参考这个值来做设置,一般不低于静态栈的两倍。我们在启动文件中默认设置的栈深(Stack_Size)为0X800(通过启动文件.s文件设置)。 ②各个函数的栈深及其调用关系:.htm文件害给出了每个函数所使用的的栈深度及其调用关系

单个函数栈深度及其调用关系

上图给出了 sys_stm32_clock_init 函数的最大栈深及其调用关系,并且列出了其所调用的函数及其所被调用的函数。 5、.map文件:对我们编程非常有帮助,这里开始才是重要讲解的。 2、.map文件分析 .map文件时编译器链接时生成的一个文件,它主要包含了交叉链接信息。通过.map文件,我们可以知道整个工程的函数调用关系、FLASH和RAM 占用情况及其详细的汇总信息。能具体到单个源文件(.c/.s)的占用情况,根据这些信息,我们可对代码进行优化。 .map文件可以分为五个部分: ①程序段交叉引用关系(Section Cross References) ②删除映像未使用的程序段(Removing Unused input sections from the image) ③映像符号表(Image Symbol Table ) ④映像内存分布图(Memory Map of the image) ⑤映像组件大小(Image component sizes) 2.1 map 文件的MDK设置 要生成map文件,我们需要在MDK的魔术棒->Listing 选项里面,最下面会有一个.map的选项区域,进行相关设置,默认是全部勾选的,如果我们想取消掉一些信息的输出,取消相关勾选即可(但不建议这样做)。 打开map文件,①确保工程编译成功;②双击工程目标,即可打开.map文件。 2.2 map文件的基础概念 为了更好分析map文件,我们先需要用到的一些基础概念进行一个简单的介绍,相关概念如下 Section:描述映像文件的代码或数据块,我们简称为 “程序段”; RO:Read Only 的缩写,包括只读数据(RO data),和代码(RO code)两部分内容,占用FLsh空间 RW:Read Write 的缩写,包含可读可写数据(RW data,有初值,且不为零),占用FLASH(存储初值)和RAM(读写操作) ZI:Zero initialized 的缩写,包含初始化为0的数据(ZI data),占用RAM空间。 .text:相当于RO code .constdata:相当于RO data .bss :相当于ZI data .data :相当于RW data 2.3map文件的组成部分说明 2.3.1程序段交叉引用关系(Section Cross References)

程序段交叉引用关系图

上图中,框出的部分,main.o(i.main) refers to sys.o(i.sys_stm32_clock_init) for

sys_stm32_clock_init,表示:main.c文件中的main函数,调用了sys.c中的sys_stm32_clock_init函数。其中:i.main 表示 main 函数的入口地址,同理 i.sys_stm32_clock_init 表示 sys_stm32_clock_init 的入口地址。

2.3.2删除映像未使用的程序段(Removing Unused input sections from image)

这部分内容描述了工程中由于未被调用而被删除的冗余程序段(函数/数据) 删除未使用的程序段 上图中,列出了所有被移除的程序段,比如 stm32h7xx_hal_usart_ex.c 文件里面的USARTEx_SetNbDataToProcess 函数就被移除了,因为该例程没用到这个函数。另外,在最后还有一个统计信息: 361 unused section(s) (total 43234 bytes) removed from the image. 表示总共移除了 361 个程序段(函数/数据),大小为 43234 字节。即给我们的MCU 节省了 43234 字节的程序空间。 为了更好地节省空间,我们一般在MDK->魔术棒->C/C++选项中,勾选One ELF Section per Function。

【注】可以得出,选项One ELF Section per Function的主要功能是对冗余函数的优化。通过这个选项,可以在最后生成的二进制文件中将冗余函数排除掉(虽然其所在的文件已经参与了编译链接),以便最大程度地优化最后生成的二进制代码。 而该选项实现的机制是将每一个函数作为一个优化的单元,而并非整个文件作为参与优化的单元。 选项One ELF Section per Function所具有的这种优化功能特别重要,尤其是在对于生成的二进制文件大小有严格要求的场合。人们习惯将一系列接口函数放在一个文件里,然后将其整个包含在工程中,即使这个文件将只有一个函数被用到。这样,最后生成的二进制文件中就有可能包含众多的冗余函数,造成了宝贵存储空间的浪费。

2.3.3 映像符号表(Image Symbol Table) 映像符号表描述了被引用的各个符号(程序段/数据)在存储器中的存储地址、类型、大小等信息。映像符号表分为两类:本地符号(Local Symbols)和全局符号(Global Symbols) 1、本地符号 本地符号记录了**用static声明的全局变量地址和大小,c文件中函数的地址和用 static 声明的函数代码大小,汇编文件中的标号地址**(作用域:限文本文件),本地符号如图: ![本地符号](https://img-blog.csdnimg.cn/849cf493124341a8aa41fbfdbb309007.png) 图中,图中红框框处部分,表示 sys.c 文件中的 sys_stm32_clock_init 函数的入口地址为:0x08002bc8,类型为:Section(程序段),大小为 0。因为:i. sys_stm32_clock_init 仅仅表示sys_stm32_clock_init 函数入口地址,并不是指令,所以没有大小。在全局符号段,会列出sys_stm32_clock_init 函数的大小。 2、全局符号 全局符号,记录了全局变量的地址和大小,C文件中函数的地址及其代码大小,汇编文件中的标号地址(作用域:全工程),全局符号如图:

在这里插入图片描述 图中红框框处部分,表示 sys.c 文件中的 sys_stm32_clock_init 函数的入口地址为: 0x08002bc9,类型为:Thumb Code(程序段),大小为 344 字节。注意,此处的地址用的 0x08002bc9,和 2.1.3.1 节的0x08002bc8 地址不符,这是因为ARM 规定 Thumb 指令集的所有指令,其最低位必须为 1,0x08002bc9 = 0x08002bc8 + 1,所以才会有 2 个不同的地址,且总是差 1,实际上就是同一个函数。

2.3.4映像内存分布图 映像文件分为加载域和运行域,一个加载域必须有至少一个运行域(可以有多个运行域),而一个程序又可以有多个加载域。加载域为映像程序的实际存储区域,而运行域则是MCU上电之后的运行状态

加载域运行域关系 由图可知,RW区也是存放在ROM里面的,在执行main函数之前,RW(有初值且不为零的变量)数据会被拷贝到RAM区,同时还会在RAM里面创建ZI区(初始化为0的变量)

了解了加载域和运行域的作用及关系,再来看映像分布图。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/8e12434077644c2d966804a5d22de4c4.png)

在这里插入图片描述 ① 处,表示映像的入口地址,也就是整个程序运行的起始地址,为:0X0800 0299。 实际地址为:0X0800 0298(Thumb 指令最低位是 1)。 ② 处,表示 LR_m_stmflash 加载域,其起始地址为:0X0800 0000;占用大小为:0X0000 2D8C;最大地址范围为:0X0002 0000。其内部包含两个运行域:ER_m_stmflash 和 RW_m_stmsram。 ③ 处,表示 ER_m_stmflash 运行域,其起始地址为:0X0800 0000;占用大小为:0X0000 2D6C;最大地址范围为:0X0002 0000;即内部 FLASH 运行域,所有需要放内部 FLASH 的代码,都应该放到这个运行域里面。对于 STM32F1/F4/F767 等开发板, MDK 使用教程 13 MAP 文件浅析 我们例程所有的代码,都是放在这个运行域的(名字可能不一样)。 ④ 处,表示 RW_m_stmsram 运行域,其起始地址为:0X2400 0000;占用大小为:0X0000 2D6C;最大地址范围为:0X0008 0000;即内部 SRAM 运行域,所有 RAM(包括 RW 和 ZI)都是放在这个运行域里面。 ⑤ 处,表示 LR_m_qspiflash 加载域,其起始地址为:0X9000 0000;占用大小为:0X0000 0720;最大地址范围为:0X0080 0000。其内部包含一个运行域:ER_m_qspiflash。 ⑥ 处,表示 ER_m_qspiflash 运行域,其起始地址为:0X9000 0000;占用大小为:0X0000 0720;最大地址范围为:0X0080 0000;即外部 QSPI FLASH 运行域,所有需要放 外部 QSPI FLASH 的代码,都应该放到这个运行域里面。 图 2.1.4.2 中,列出了所有加载域及其运行域的具体内存分布,我们可以很方便的查看 任何一个函数所在的运行域、入口地址、占用空间等信息。如 sys_stm32_clock_init 函数: 该函数在 ER_m_stmflash 运行域;入口地址为:0X0800 2BC8;大小为:0X168 字节;是 sys.c 里面的函数。了解这些信息,对我们分析及优化程序非常有用。

2.3.5映像组件大小

映像组件大小,给出了整个映像所有代码(.o)占用空间的汇总信息,对我们比较有用 在这里插入图片描述 在这里插入图片描述 上图中,框出的三处信息对我们比较有用,接下来分别介绍: ① 处,表示.c/.s 文件生成对象所占空间大小(单位:字节,下同),即.c/.s 文件编 译后所占代码空间的大小。每个项所代表的意义如下: Code(inc.data):表示包含内联数据(inc.data)后的代码大小。如 delay.o(即 delay.c)所占的 Code 大小为 142 字节,其中 8 字节是内联数据。 RO Data:表示只读数据所占的空间大小,一般是指 const 修饰的数据大小。

RW Data:表示有初值(且非 0)的可读写数据所占的空间大小,它同时占用 FLASH 和 RAM 空间。 ZI Data:表示初始化为 0 的可读写数据所占空间大小,它只占用 RAM 空间。 Debug:表示调试数据所占的空间大小,如调试输入节及符号和字符串。 Object Totals:表示以上部分链接到一起后,所占映像空间的大小。 (incl.Generated):表示链接器生产的映像内容大小,它包含在 Object Totals 里 面了,这里仅仅是单独列出,我们一般不需要关心。 (incl.Padding):表示链接器根据需要插入填充以保证字节对齐的数据所占空间 的大小,它也包含在 Object Totals 里面了,这里单独列出,一般无需关心。 ② 处,表示被提取的库成员(.lib)添加到映像中的部分所占空间大小。各项意义同 ①中的说明。我们一般只用看 Library Totals 来分析库所占空间的大小即可。 ③ 处,表示本工程全部程序汇总后的占用情况。其中: Grand Totals:表示整个映像所占空间大小。 ELF Image Totals:表示 ELF 可执行链接格式映像文件的大小,一般和 Grand Totals 一样大小。 ROM Totals:表示整个映像所需要的 ROM 空间大小,不含 ZI 和 Debug 数据。 Total RO Size:表示 Code 和 RO 数据所占空间大小,本例程为:13452 字节。 Total RW Size:表示 RW 和 ZI 数据所占空间大小,即本映像所需 SRAM 空间的大 小,本例程为:3032 字节。 Total ROM Size:表示 Code、RO 和 RW 数据所占空间大小,即本映像所需 FLASH 空间的大小,本例程为:13484 字节。 图 2.1.5.1 中,我们未框出的:Library Name 部分,实际和②处是一个意思,只是 Library Name 说明了②处的那些.o 文件来自什么库,这里实际上就是:fpinit.o 来自 fz_wv.l 库,其 他部分来自 c_w.l 库。fz_wv.l 和 c_w.l 是库名字



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有